// ------------------------------------------------------------------------------------------
// Licensed by Interprise Solutions.
// http://www.InterpriseSolutions.com
// For details on this license please visit  the product homepage at the URL above.
// THE ABOVE NOTICE MUST REMAIN INTACT.
// ------------------------------------------------------------------------------------------
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Security;
using Microsoft.Web.Services3.Xml;
using Microsoft.Web.Services3.Security.Xml;
using Microsoft.Web.Services3.Design;
using Microsoft.Web.Services3.Security.Tokens;
using System.Xml;
using System.Collections.Generic;

namespace InterpriseSuiteEcommerceCommon.InterpriseIntegration.Web.WSE
{
    /// <summary>
    /// Summary description for InterpriseSuiteEcommerceServicePolicy
    /// </summary>
    public class InterpriseSuiteEcommerceServicePolicy : Policy
    {
        public InterpriseSuiteEcommerceServicePolicy() : base(new ServiceSecurityAssertion()) { }

        public class ServiceSecurityAssertion : SecurityPolicyAssertion
        {
            public override SoapFilter CreateClientInputFilter(FilterCreationContext context)
            {
                return null;
            }

            public override SoapFilter CreateClientOutputFilter(FilterCreationContext context)
            {
                return null;
            }

            public override SoapFilter CreateServiceInputFilter(FilterCreationContext context)
            {
                return new StoreServiceSecurityInputFilter();
            }

            public override SoapFilter CreateServiceOutputFilter(FilterCreationContext context)
            {
                return null;
            }

            public override IEnumerable<KeyValuePair<string, Type>> GetExtensions()
            {
                IEnumerable<KeyValuePair<string, Type>> extensions = base.GetExtensions();
                return extensions;
            }

            public override void ReadXml(XmlReader reader, IDictionary<string, Type> extensions)
            {
                if (reader == null)
                    throw new ArgumentNullException("reader");
                if (extensions == null)
                    throw new ArgumentNullException("extensions");

                // determine the name of the extension
                string tagName = null;
                foreach (string extName in extensions.Keys)
                {
                    if (extensions[extName] == typeof(ServiceSecurityAssertion))
                    {
                        tagName = extName;
                        break;
                    }
                }

                // read the first element (maybe empty)
                reader.ReadStartElement(tagName);
            }
        }

        private class StoreServiceSecurityInputFilter : ReceiveSecurityFilter
        {
            public StoreServiceSecurityInputFilter() : base(string.Empty, false) { }

            public override SoapFilterResult ProcessMessage(SoapEnvelope envelope)
            {
                return base.ProcessMessage(envelope);
            }

            public override void ValidateMessageSecurity(SoapEnvelope envelope, Microsoft.Web.Services3.Security.Security security)
            {
                if (null == security)
                {
                    throw new SecurityFault(SecurityFault.FailedAuthenticationMessage, SecurityFault.FailedAuthenticationCode);
                }

                // do nothing for the meantime...
                UsernameToken signedToken = GetSigningToken(envelope, security) as UsernameToken;

                if (null == signedToken || signedToken.PasswordOption == PasswordOption.SendPlainText)
                {
                    throw new SecurityFault(SecurityFault.FailedAuthenticationMessage, SecurityFault.FailedAuthenticationCode);
                }

                // Make sure only one SCT(?) is included in the message
                int tokenCount = 0;
                foreach (SecurityToken token in security.Tokens)
                {
                    if (null != token as UsernameToken)
                    {
                        tokenCount++;
                    }
                }

                if (tokenCount != 1)
                {
                    throw new Microsoft.Web.Services3.Security.SecurityFault(SecurityFault.FailedAuthenticationMessage, SecurityFault.FailedAuthenticationCode);
                }
            }

            private SecurityToken GetSigningToken(SoapEnvelope envelop, Microsoft.Web.Services3.Security.Security security)
            {
                foreach (ISecurityElement element in security.Elements)
                {
                    MessageSignature sig = element as MessageSignature;

                    if (null != sig)
                    {
                        if (CheckSignature(envelop, security, sig))
                        {
                            return sig.SigningToken;
                        }
                    }
                }

                return null;
            }

            private bool CheckSignature(SoapEnvelope envelope, Microsoft.Web.Services3.Security.Security security, MessageSignature signature)
            {
                // Now verify which parts of the message were actually signed
                SoapContext context = envelope.Context;

                SignatureOptions actualOptions = signature.SignatureOptions;
                SignatureOptions expectedOptions = SignatureOptions.IncludeSoapBody;

                if (null != security && null != security.Timestamp)
                {
                    expectedOptions = expectedOptions | SignatureOptions.IncludeTimestamp;
                }

                // The <Action> and <To> are required addressing elements
                expectedOptions = expectedOptions | SignatureOptions.IncludeAction;
                expectedOptions = expectedOptions | SignatureOptions.IncludeTo;

                if (null != context.Addressing.FaultTo && null != context.Addressing.FaultTo.TargetElement)
                {
                    expectedOptions = expectedOptions | SignatureOptions.IncludeFaultTo;
                }

                if (null != context.Addressing.From && null != context.Addressing.From.TargetElement)
                {
                    expectedOptions = expectedOptions | SignatureOptions.IncludeFrom;
                }

                if (null != context.Addressing.MessageID && null != context.Addressing.MessageID.TargetElement)
                {
                    expectedOptions = expectedOptions | SignatureOptions.IncludeMessageId;
                }

                if (null != context.Addressing.RelatesTo && null != context.Addressing.RelatesTo.TargetElement)
                {
                    expectedOptions = expectedOptions | SignatureOptions.IncludeRelatesTo;
                }

                if (null != context.Addressing.ReplyTo && null != context.Addressing.ReplyTo.TargetElement)
                {
                    expectedOptions = expectedOptions | SignatureOptions.IncludeReplyTo;
                }

                return (expectedOptions & actualOptions) == expectedOptions;
            }
        }
    }
}
